#include <webx/route.h>

class Compile : public webx::ProcessBase
{
protected:
	int process();
};

HTTP_WEBAPP(Compile, "/compile/${filename}")

int Compile::process()
{
	TaskQueue* queue = TaskQueue::Instance();
	string path = app->getPath() + "app/compile/cpp/";

	if ((queue->size() << 1) >= queue->getThreads())
	{
		Sleep(100);

		return simpleResponse(XG_SYSBUSY);
	}

	time_t now = time(NULL);
	vector<string> pathlist;

	pathlist.push_back(path);
	pathlist.push_back(path + ".bin/");

	for (auto path : pathlist)
	{
		time_t utime;
		vector<string> vec;

		if (stdx::GetFolderContent(vec, path, eFILE) < 100) break;

		for (string& name : vec)
		{
			if (name == "config.yml" || name == "config.cpp") continue;

			name = path + name;
			utime = path::mtime(name);
	
			if (utime + 300 < now)
			{
				LogTrace(eINF, "file[%s][%s] has expired", name.c_str(), DateTime(utime).toString().c_str());
	
				path::remove(name);
			}
		}
	}

	param_string(code);
	param_string(param);

	code = stdx::trim(code);
	param = stdx::trim(param);

	webx::CheckFilePath(param, 0, 1024);

	if (code.empty() || code.length() > 256 * 1024) return simpleResponse(XG_PARAMERR);

	int port;
	string ip;
	bool crypted;
	HttpRequest request;

	auto translate = [](string msg){
		msg = stdx::replace(msg, "&", "&amp;");
		msg = stdx::replace(msg, ">", "&gt;");
		msg = stdx::replace(msg, "<", "&lt;");
		msg = stdx::replace(msg, "\"", "&quot;");

		return msg;
	};

	auto getHttpSocket = [&](HttpRequest& request){
		size_t pos = code.find('\n');

		if (pos == string::npos) return false;

		string data = stdx::trim(code.substr(pos + 1));
		string head = stdx::replace(code.substr(0, pos), "\t", " ");

		pos = head.find(' ');

		if (pos == string::npos) return false;

		string path = head.substr(pos + 1);
		string method = head.substr(0, pos);

		path = stdx::trim(path);
		pos = path.find(' ');

		if (pos != string::npos) path = path.substr(0, pos);

		map<string, E_HTTP_METHOD> methodmap = {
			{"GET", eGET},
			{"PUT", ePUT},
			{"POST", ePOST},
			{"HEAD", eHEAD},
			{"DELETE", eDELETE},
			{"OPTIONS", eOPTION}
		};

		auto it = methodmap.find(method);

		if (it == methodmap.end()) return false;

		request.init(path, it->second);

		size_t end = 0;
		string contype;
		sp<Socket> sock;
		vector<string> vec = stdx::split(data, "\n");
		
		for (size_t i = 0; i < vec.size(); i++)
		{
			string line = vec[i];

			if (stdx::trim(line).empty()) continue;

			pos = line.find(':');

			if (pos == string::npos || pos > 64) break;

			string key = stdx::trim(line.substr(0, pos));
			string val = stdx::trim(line.substr(pos + 1));

			for (char tag : key)
			{
				if (isalnum(tag) || tag == '-' || tag == '_') continue;

				key.clear();

				break;
			}

			if (key.empty() || val.empty()) break;

			if (stdx::toupper(key.c_str()) == "CONTENT-TYPE")
			{
				contype = val;
			}

			if (stdx::toupper(key.c_str()) == "HOST")
			{
				string host;
				string path;

				HttpRequest::GetInfoFromURL(val, host, path, ip, port, crypted);

				if (ip.empty() || port <= 0) return false;

				request.setHeadHost(ip, port);
			}
			else
			{
				request.setHeadValue(key, val);
			}

			end = i + 1;
		}

		if (port <= 0) return false;

		string payload;

		while (end < vec.size())
		{
			payload += vec[end++] + "\n";
		}

		payload = stdx::trim(payload);

		if (payload.length() > 0)
		{
			if (contype.empty() && JsonElement(payload).isObject())
			{
				request.setContentType("application/json");
			}

			request.setPayload(payload);
		}

		request.setHeadValue("X-Forwarded-For", response->getClientHost());

		return true;
	};

	if (getHttpSocket(request))
	{
		string errmsg;
		sp<HttpResponse> res = request.getResponse(ip, port, crypted, 1000);

		if (res)
		{
			SmartBuffer data = res->getResult();

			if (data.isNull())
			{
				errmsg = stdx::format("返回内容为空[状态码：%d]", res->getStatus());
			}
			else
			{
				json["output"] = translate(data.str());
				json["result"] = data.str();
			}

			Sleep(100);
		}
		else
		{
			errmsg = "接口请求失败（请检查服务是否正常）";
		}

		if (errmsg.length() > 0)
		{
			json["output"] = "<red>" + errmsg + "</red>";
			json["result"] = errmsg;
		}

		json["code"] = XG_OK;
	}
	else
	{
		char buffer[64 * 1024];
		string filename = path;

		stCharBuffer name = MD5GetEncodeString(code.c_str(), code.length(), true);

		stdx::append(filename, "%s.cpp", name.val);

		if (path::size(filename) > 0)
		{
			Sleep(100);
		}
		else
		{
			File file;
			size_t pos;
			size_t end;

			code = stdx::replace(code, "\r", "");
			pos = code.find("\n\n");
			end = code.find("{");
			
			if (pos == string::npos || pos > end)
			{
				code = "#include \"config.cpp\"\n" + code;
			}
			else
			{
				code = code.substr(0, pos) + "\n#include \"config.cpp\"" + code.substr(pos + 1);
			}

			file.open(filename , "w+");

			if (file.getHandle() == NULL) return simpleResponse(XG_SYSERR);

			if (file.write(code.c_str(), code.length()) <= 0) return simpleResponse(XG_SYSERR);
		}

		string output;
		string errpath = path;

		stdx::append(errpath, "%s.out", name.val);

#ifdef XG_LINUX
		string exepath = stdx::translate("$CPPWEB_PRODUCT_HOME/bin/cppshell");

		stdx::format(code, "ulimit -c 0 && %s %s %s 2>%s", stdx::quote(exepath).c_str(), stdx::quote(filename).c_str(), param.c_str(), stdx::quote(errpath).c_str());
#else
		string exepath = stdx::translate("$CPPWEB_PRODUCT_HOME/bin/cppshell.exe");

		stdx::format(code, "%s %s %s 2>%s", stdx::quote(exepath).c_str(), stdx::quote(filename).c_str(), param.c_str(), stdx::quote(errpath).c_str());
#endif
		if (cmdx::RunCommand(code, buffer, sizeof(buffer), true, false) < 0) return simpleResponse(XG_SYSERR);

		stdx::GetFileContent(output, errpath);

		output = stdx::trim(output);

		auto getTranslateString = [&](string output, bool flag = true){
			if (flag)
			{
				output = translate(output);
			}
			else
			{
				output = stdx::replace(output, "\e[31m", "");
				output = stdx::replace(output, "\e[32m", "");
				output = stdx::replace(output, "\e[33m", "");
				output = stdx::replace(output, "\e[34m", "");
				output = stdx::replace(output, "\e[37m", "");
			}

			output = stdx::replace(output, filename, "code.cpp");

			return stdx::replace(output, path::name(filename), "code.cpp");
		};

		if (output.empty())
		{
			output = getTranslateString(buffer);
			output = "<font>" + output + "</font>";
			output = stdx::replace(output, "\e[31m", "</font><font color='#C00'>");
			output = stdx::replace(output, "\e[32m", "</font><font color='#090'>");
			output = stdx::replace(output, "\e[33m", "</font><font color='#BB0'>");
			output = stdx::replace(output, "\e[34m", "</font><font color='#00B'>");
			output = stdx::replace(output, "\e[37m", "</font><font color='#888'>");

			json["result"] = getTranslateString(buffer, false);
			json["code"] = XG_OK;
		}
		else
		{
			if (output.find("SYSTEM ERROR[-5]") == string::npos)
			{
				output = getTranslateString(output);

				json["code"] = XG_PARAMERR;
			}
			else
			{
				json["code"] = XG_TIMEOUT;

				output.clear();
			}
		}

		json["output"] = output;
	}

	out << json;

	return XG_OK;
}